//-----------------------------------------------------------------------------
//
// (c) Copyright 2001, 2002, 2003, 2004, 2005, 2007, 2008, 2009 Xilinx, Inc. All rights reserved.
//
// This file contains confidential and proprietary information
// of Xilinx, Inc. and is protected under U.S. and
// international copyright and other intellectual property
// laws.
//
// DISCLAIMER
// This disclaimer is not a license and does not grant any
// rights to the materials distributed herewith. Except as
// otherwise provided in a valid license issued to you by
// Xilinx, and to the maximum extent permitted by applicable
// law: (1) THESE MATERIALS ARE MADE AVAILABLE "AS IS" AND
// WITH ALL FAULTS, AND XILINX HEREBY DISCLAIMS ALL WARRANTIES
// AND CONDITIONS, EXPRESS, IMPLIED, OR STATUTORY, INCLUDING
// BUT NOT LIMITED TO WARRANTIES OF MERCHANTABILITY, NON-
// INFRINGEMENT, OR FITNESS FOR ANY PARTICULAR PURPOSE; and
// (2) Xilinx shall not be liable (whether in contract or tort,
// including negligence, or under any other theory of
// liability) for any loss or damage of any kind or nature
// related to, arising under or in connection with these
// materials, including for any direct, or any indirect,
// special, incidental, or consequential loss or damage
// (including loss of data, profits, goodwill, or any type of
// loss or damage suffered as a result of any action brought
// by a third party) even if such damage or loss was
// reasonably foreseeable or Xilinx had been advised of the
// possibility of the same.
//
// CRITICAL APPLICATIONS
// Xilinx products are not designed or intended to be fail-
// safe, or for use in any application requiring fail-safe
// performance, such as life-support or safety devices or
// systems, Class III medical devices, nuclear facilities,
// applications related to the deployment of airbags, or any
// other applications that could lead to death, personal
// injury, or severe property or environmental damage
// (individually and collectively, "Critical
// Applications"). Customer assumes the sole risk and
// liability of any use of Xilinx products in Critical
// Applications, subject only to applicable laws and
// regulations governing limitations on product liability.
//
// THIS COPYRIGHT NOTICE AND DISCLAIMER MUST BE RETAINED AS
// PART OF THIS FILE AT ALL TIMES.
//
//-----------------------------------------------------------------------------
// Project    : Spartan-6 Integrated Block for PCI Express
// File       : PIO_32_TX_ENGINE.v
// Description: 32 bit LocalLink Transmit Unit.
//
//-----------------------------------------------------------------------------

`timescale 1ns/1ns

module PIO_32_TX_ENGINE # (
    parameter UP_XFER_WIDTH = 12
) (
    input                   clk,
    input                   rst_n,

    // AXIS
    input                   s_axis_tx_tready,
    output  reg [31:0]      s_axis_tx_tdata,
    output  reg [3:0]       s_axis_tx_tkeep,
    output  reg             s_axis_tx_tlast,
    output  reg             s_axis_tx_tvalid,
    output                  tx_src_dsc,

    input                   req_compl_i,
    input                   req_compl_with_data_i,
    output reg              compl_done_o,

    input       [2:0]       req_tc_i,
    input                   req_td_i,
    input                   req_ep_i,
    input       [1:0]       req_attr_i,
    input       [9:0]       req_len_i,
    input       [15:0]      req_rid_i,
    input       [7:0]       req_tag_i,
    input       [7:0]       req_be_i,
    input       [12:0]      req_addr_i,

    output wire [10:0]      rd_addr_o,
    output wire [3:0]       rd_be_o,
    input       [31:0]      rd_data_i,

    input       [15:0]      completer_id_i,
    input                   cfg_bus_mstr_enable_i,

    output reg              rd_data_en,
    // Upstream Write, added by zcz
    input                   up_wr_req_i,
    output                  up_wr_start_o,
    input    [29:0]         up_pc_mem_addr_i,
    input    [31:0]         up_wr_data_i,
    input                   up_wr_data_rdy_i,
    output                  up_wr_done_o
);

  // Clock-to-out delay
  localparam TCQ                    = 1;

  // TLP Header format/type values
  localparam PIO_32_CPLD_FMT_TYPE   = 7'b10_01010;
  localparam PIO_32_CPL_FMT_TYPE    = 7'b00_01010;
  // Added by zcz for Upstream NMWr
  localparam PIO_32_WR32_FMT_TYPE   = 7'b10_00000;

  // State values
  localparam PIO_32_TX_RST_STATE    = 3'd0;
  localparam PIO_32_TX_CPL_CPLD_DW1 = 3'd1;
  localparam PIO_32_TX_CPL_CPLD_DW2 = 3'd2;
  localparam PIO_32_TX_CPLD_DW3     = 3'd3;
  localparam PIO_32_TX_WAIT_STATE   = 3'd4;
  localparam PIO_32_TX_MWR_DW1      = 3'd5;
  localparam PIO_32_TX_MWR_DW2      = 3'd6;
  localparam PIO_32_TX_MWR_DATA     = 3'd7;



  // Local registers
  reg [11:0]          byte_count;
  reg [6:0]           lower_addr;
  reg [2:0]           state;
  reg                 cpl_w_data;
  reg                 req_compl_q;
  reg                 req_compl_with_data_q;

  // Added by zcz
  reg                 up_wr_xfering; // 1: writing data
  reg                 up_wr_start;   // a puls to indicate wr is beginning.
  reg                 up_wr_done_r;
  //reg  [31:0]         up_pc_mem_addr_r;
  reg  [4:0]           up_wr_data_cnt;

  assign up_wr_done_o   = up_wr_done_r;
  assign up_wr_start_o  = up_wr_start;
  //
  // Unused discontinue signal
  //

  assign tx_src_dsc = 1'b0;

  //
  // Present address and byte enable to memory module
  //
  assign rd_addr_o = req_addr_i[12:2];
  assign rd_be_o   = req_be_i[3:0];


  //
  // Calculate byte count based on byte enable
  //
  always @* begin
    casex (rd_be_o[3:0])
      4'b1xx1 : byte_count = 12'h004;
      4'b01x1 : byte_count = 12'h003;
      4'b1x10 : byte_count = 12'h003;
      4'b0011 : byte_count = 12'h002;
      4'b0110 : byte_count = 12'h002;
      4'b1100 : byte_count = 12'h002;
      4'b0001 : byte_count = 12'h001;
      4'b0010 : byte_count = 12'h001;
      4'b0100 : byte_count = 12'h001;
      4'b1000 : byte_count = 12'h001;
      4'b0000 : byte_count = 12'h001;
    endcase
  end

  //
  // Calculate lower address based on byte enable
  // This assumes that only PIO write responses ask
  // for a completion without data.
  //
  always @* begin
    casex ({req_compl_with_data_i, rd_be_o[3:0]})
      5'b0_xxxx : lower_addr = 8'h0;
      5'bx_0000 : lower_addr = {req_addr_i[6:2], 2'b00};
      5'bx_xxx1 : lower_addr = {req_addr_i[6:2], 2'b00};
      5'bx_xx10 : lower_addr = {req_addr_i[6:2], 2'b01};
      5'bx_x100 : lower_addr = {req_addr_i[6:2], 2'b10};
      5'bx_1000 : lower_addr = {req_addr_i[6:2], 2'b11};
    endcase
  end

  reg req_compl_ack;
  always @(posedge clk) begin
      if (!rst_n) begin
          req_compl_q           <= #TCQ 1'b0;
          req_compl_with_data_q <= #TCQ 1'b1;
      end else begin
          req_compl_with_data_q <= #TCQ req_compl_with_data_i;
          if ( req_compl_i ) begin
              req_compl_q   <= #TCQ 1'b1;
          end else begin
              if ( req_compl_ack ) begin
                  req_compl_q       <= #TCQ 1'b0;
              end
          end
    end
  end

  //
  //  Generate Completion with 1 DW Payload or Completion with
  //  no data
  //
  always @(posedge clk) begin
    if (!rst_n) begin

      s_axis_tx_tlast   <= #TCQ 1'b0;
      s_axis_tx_tvalid  <= #TCQ 1'b0;
      s_axis_tx_tdata   <= #TCQ 32'h0;
      s_axis_tx_tkeep   <= #TCQ 4'hF;

      req_compl_ack     <= #TCQ 1'b0;
      compl_done_o      <= #TCQ 1'b0;
      up_wr_done_r      <= #TCQ 1'b0;
      up_wr_xfering     <= #TCQ 1'b0;
      up_wr_start       <= #TCQ 1'b0;
      up_wr_data_cnt    <= #TCQ 5'd0;
      rd_data_en        <= #TCQ 1'b0;

      state             <= #TCQ PIO_32_TX_RST_STATE;
    end else begin

      case (state)
        PIO_32_TX_RST_STATE : begin
            compl_done_o      <= #TCQ 1'b0;
            up_wr_done_r      <= #TCQ 1'b0;
            rd_data_en        <= #TCQ 1'b0;

          if (req_compl_q && req_compl_with_data_q) begin
            // Begin a CplD TLP
            s_axis_tx_tlast  <= #TCQ 1'b0;
            s_axis_tx_tvalid <= #TCQ 1'b1;
            s_axis_tx_tdata  <= #TCQ {1'b0,
                                      PIO_32_CPLD_FMT_TYPE,
                                      {1'b0},
                                      req_tc_i,
                                      {4'b0},
                                      req_td_i,
                                      req_ep_i,
                                      req_attr_i,
                                      {2'b0},
                                      req_len_i
                                      };
            cpl_w_data       <= #TCQ req_compl_with_data_q;
            up_wr_xfering    <= #TCQ 1'b0;
            rd_data_en       <= #TCQ 1'b1;
            req_compl_ack    <= #TCQ 1'b1;
            state            <= #TCQ PIO_32_TX_CPL_CPLD_DW1;
          end else if (req_compl_q && !req_compl_with_data_q) begin
            // Begin a Cpl TLP
            s_axis_tx_tlast  <= #TCQ 1'b0;
            s_axis_tx_tvalid <= #TCQ 1'b1;
            s_axis_tx_tdata  <= #TCQ {1'b0,
                                      PIO_32_CPL_FMT_TYPE,
                                      {1'b0},
                                      req_tc_i,
                                      {4'b0},
                                      req_td_i,
                                      req_ep_i,
                                      req_attr_i,
                                      {2'b0},
                                      req_len_i
                                      };
            cpl_w_data       <= #TCQ req_compl_with_data_q;
            up_wr_xfering    <= #TCQ 1'b0;
            req_compl_ack    <= #TCQ 1'b1;
            state            <= #TCQ PIO_32_TX_CPL_CPLD_DW1;
        end else if ( up_wr_req_i && cfg_bus_mstr_enable_i ) begin
            s_axis_tx_tlast  <= #TCQ 1'b0;
            s_axis_tx_tvalid <= #TCQ 1'b1;
            s_axis_tx_tdata  <= #TCQ {1'b0,
                                      PIO_32_WR32_FMT_TYPE,
                                      {1'b0},
                                      req_tc_i,
                                      {4'b0},
                                      req_td_i,
                                      req_ep_i,
                                      req_attr_i,
                                      {2'b0},
                                      10'd32 //req_len_i
                                      };
            up_wr_xfering     <= #TCQ 1'b1;
            up_wr_start       <= #TCQ 1'b1;
            up_wr_data_cnt    <= #TCQ 5'd0;
            state             <= #TCQ PIO_32_TX_MWR_DW1;

          end else begin
            s_axis_tx_tlast   <= #TCQ 1'b0;
            s_axis_tx_tvalid  <= #TCQ 1'b0;
            s_axis_tx_tdata   <= #TCQ 32'h0;
            s_axis_tx_tkeep   <= #TCQ 4'hF;
            up_wr_xfering     <= #TCQ 1'b0;
            state             <= #TCQ PIO_32_TX_RST_STATE;
          end
        end // PIO_32_TX_RST_STATE

        PIO_32_TX_CPL_CPLD_DW1 : begin
            req_compl_ack   <= #TCQ 1'b0;
          if (s_axis_tx_tready) begin
            // Output next DW of TLP
            s_axis_tx_tlast  <= #TCQ 1'b0;
            s_axis_tx_tvalid <= #TCQ 1'b1;
            s_axis_tx_tdata  <= #TCQ {completer_id_i,
                                      {3'b0},
                                      {1'b0},
                                      byte_count
                                      };
            state            <= #TCQ PIO_32_TX_CPL_CPLD_DW2;
          end else begin
            // Wait for core to accept previous DW
            state            <= #TCQ PIO_32_TX_CPL_CPLD_DW1;
          end
        end // PIO_32_TX_CPL_CPLD_DW1

        PIO_32_TX_CPL_CPLD_DW2 : begin
          if (s_axis_tx_tready) begin
            // Output next DW of TLP
            s_axis_tx_tlast  <= #TCQ 1'b0;
            s_axis_tx_tvalid <= #TCQ 1'b1;
            s_axis_tx_tdata  <= #TCQ {req_rid_i,
                                      req_tag_i,
                                      {1'b0},
                                      lower_addr
                                      };

            if (cpl_w_data) begin
              // For a CplD, there is one more DW
              s_axis_tx_tlast <= #TCQ 1'b0;
              state           <= #TCQ PIO_32_TX_CPLD_DW3;
            end else begin
              // For a Cpl, this is the final DW
              s_axis_tx_tlast <= #TCQ 1'b1;
              state           <= #TCQ PIO_32_TX_WAIT_STATE;
            end
          end else begin
            // Wait for core to accept previous DW
            state            <= #TCQ PIO_32_TX_CPL_CPLD_DW2;
          end
        end // PIO_32_TX_CPL_CPLD_DW2

        PIO_32_TX_CPLD_DW3 : begin
          if (s_axis_tx_tready) begin
            // Output next DW of TLP
            s_axis_tx_tlast  <= #TCQ 1'b1;
            s_axis_tx_tvalid <= #TCQ 1'b1;
            s_axis_tx_tdata  <= #TCQ { rd_data_i[7:0], rd_data_i[15:8], rd_data_i[23:16], rd_data_i[31:24] };
            rd_data_en       <= #TCQ 1'b0;
            state            <= #TCQ PIO_32_TX_WAIT_STATE;
          end else begin
            // Wait for core to accept previous DW
            state            <= #TCQ PIO_32_TX_CPLD_DW3;
          end
        end // PIO_32_TX_CPLD_DW3

        PIO_32_TX_MWR_DW1 : begin
            up_wr_start      <= #TCQ 1'b0;
            if (s_axis_tx_tready) begin
                s_axis_tx_tlast   <= #TCQ 1'b0;
                s_axis_tx_tvalid  <= #TCQ 1'b1;
                s_axis_tx_tdata   <= #TCQ { completer_id_i, 8'b0, 8'hFF };
                state             <= #TCQ PIO_32_TX_MWR_DW2;
            end else begin
                state             <= #TCQ PIO_32_TX_MWR_DW1;
            end
        end
        PIO_32_TX_MWR_DW2 : begin
            up_wr_start     <= #TCQ 1'b0;
            if (s_axis_tx_tready) begin
                s_axis_tx_tlast  <= #TCQ 1'b0;
                s_axis_tx_tvalid <= #TCQ 1'b1;
                s_axis_tx_tdata  <= #TCQ { up_pc_mem_addr_i, 2'b0 };
                state            <= #TCQ PIO_32_TX_MWR_DATA;
            end else begin
                state            <= #TCQ PIO_32_TX_MWR_DW2;
            end
        end
        PIO_32_TX_MWR_DATA : begin
            up_wr_start     <= #TCQ 1'b0;
            if (s_axis_tx_tready && up_wr_data_rdy_i ) begin
                s_axis_tx_tvalid <= #TCQ 1'b1;
                s_axis_tx_tdata  <= #TCQ { up_wr_data_i[7:0], up_wr_data_i[15:8], up_wr_data_i[23:16], up_wr_data_i[31:24] };
                case (up_wr_data_cnt[4:0] )
                    5'd29 : begin
                        s_axis_tx_tlast <= #TCQ 1'b0;
                        up_wr_done_r    <= 1'b1;
                        state           <= #TCQ PIO_32_TX_MWR_DATA;
                    end
                    5'd31 : begin
                        s_axis_tx_tlast <= #TCQ 1'b1;
                        up_wr_done_r    <= 1'b0;
                        state           <= #TCQ PIO_32_TX_WAIT_STATE;
                    end
                    default: begin
                        s_axis_tx_tlast <= #TCQ 1'b0;
                        up_wr_done_r    <= 1'b0;
                        state           <= #TCQ PIO_32_TX_MWR_DATA;
                    end
                endcase
                up_wr_data_cnt   <= #TCQ up_wr_data_cnt + 5'd1;
            end else begin
                s_axis_tx_tvalid <= #TCQ 1'b0;
                state            <= #TCQ PIO_32_TX_MWR_DATA;
            end
        end
        PIO_32_TX_WAIT_STATE : begin
          if (s_axis_tx_tready) begin
            // Core has accepted final DW of TLP
            s_axis_tx_tlast  <= #TCQ 1'b0;
            s_axis_tx_tvalid <= #TCQ 1'b0;
            // compl_done_o     <= #TCQ 1'b1;
            s_axis_tx_tdata  <= #TCQ 32'h0;
            state            <= #TCQ PIO_32_TX_RST_STATE;
            if ( up_wr_xfering )
                up_wr_done_r <= 1'b0;
            else
                compl_done_o <= 1'b1;
          end else begin
            // Wait for core to accept previous DW
            state            <= #TCQ PIO_32_TX_WAIT_STATE;
          end
        end // PIO_32_TX_WAIT_STATE
        default:
          state              <= #TCQ PIO_32_TX_RST_STATE;
      endcase // (state)
    end // rst_n
  end

endmodule // PIO_32_TX_ENGINE

